Skip to content

原型链了解(推荐 Es6,过时禁止使用的)

首先了解下什么是原型链,JavaScript 中所有的对象都有一个内置属性,称为它的 prototype(原型)。它本身是一个对象,故原型对象也会有它自己的原型,逐渐构成了原型链。原型链终止于拥有 null 作为其原型的对象上。

备注:

指向对象原型的属性并不是 prototype。它的名字不是标准的,但实际上所有浏览器都使用 __proto__。访问对象原型的标准方法是 Object.getPrototypeOf()。

原型链继承是通过将子类的原型对象指向父类的实例来实现继承。

  1. 原型链继承

    • 原型链继承是通过将子类的原型对象指向父类的实例来实现继承。
    javascript
    function Parent() {
      this.name = "Parent";
    }
    Parent.prototype.sayHello = function () {
      console.log("Hello, I am " + this.name);
    };
    
    function Child() {}
    Child.prototype = new Parent(); // 将子类的原型对象指向父类的实例
    var child = new Child();
    child.sayHello(); // 输出 "Hello, I am Parent"

缺陷例子

  • 原型链污染: 在原型链继承中,如果不小心修改了原型对象,那么所有继承自该原型的对象都会受到影响。这可能会导致意外的行为,特别是在多人协作或复杂代码中。

  • 无法传递参数: 在使用原型链继承时,子类无法直接向父类构造函数传递参数,因为父类构造函数在子类原型链中,并不会在子类实例化时调用。这导致所有实例共享同一份父类属性,不能传入不同的参数。

  • 不能完全重写父类方法: 如果尝试在子类中重写父类的方法,实际上只是在子类实例中创建了一个同名的新方法,而不是真正意义上的重写。这可能导致混淆和错误,因为子类的行为可能与预期的不同。

  • 无法实现多重继承: JavaScript 的原型链只允许单一继承,无法直接实现多重继承,这在某些情况下可能会限制代码的组织和复用。

  1. 原型链污染

    js
    // 父类构造函数
    function Parent() {
      this.colors = ["red", "blue", "green"];
    }
    
    // 在父类原型中定义方法
    Parent.prototype.sayHello = function () {
      console.log("Hello from Parent");
    };
    
    // 子类构造函数
    function Child() {}
    
    // 使用原型链继承
    Child.prototype = new Parent(); // 将子类的原型对象指向父类的实例
    
    // 创建子类实例
    var child = new Child();
    var child2 = new Child();
    
    // 修改 color
    child.colors.push("black");
    
    // 修改父类原型中的方法
    Parent.prototype.sayHello = function () {
      console.log("Modified Hello from Parent");
    };
    
    // 子类实例也受到了影响
    child2.sayHello(); // 输出: Modified Hello from Parent
    console.log(child2.colors); // ["red", "blue", "green", "black"]
  2. 无法传递参数

    js
    // 父类构造函数
    function Parent(name) {
      this.name = name;
    }
    
    // 子类构造函数
    function Child() {}
    
    // 使用原型链继承
    Child.prototype = new Parent("Parent Name");
    
    // 创建子类实例
    var child = new Child();
    
    console.log(child.name); // 输出: Parent Name
  3. 不能完全重写父类方法

    js
    // 父类构造函数
    function Parent() {}
    
    // 在父类原型中定义方法
    Parent.prototype.sayHello = function () {
      console.log("Hello from Parent");
    };
    
    // 子类构造函数
    function Child() {}
    
    // 使用原型链继承
    Child.prototype = new Parent();
    
    // 重写父类方法
    Child.prototype.sayHello = function () {
      console.log("Hello from Child");
    };
    
    // 创建子类实例
    var child = new Child();
    
    // 子类实例调用重写后的方法
    child.sayHello(); // 输出: Hello from Child
  4. 无法实现多重继承

    js
    // 父类1构造函数
    function Parent1() {
      this.name1 = "Parent1";
    }
    
    // 父类2构造函数
    function Parent2() {
      this.name2 = "Parent2";
    }
    
    // 子类构造函数
    function Child() {}
    
    // 使用原型链继承
    Child.prototype = new Parent1();
    
    // 尝试实现多重继承,但只能继承一个父类
    Child.prototype = new Parent2(); // 覆盖了之前的继承
    
    // 创建子类实例
    var child = new Child();
    
    console.log(child.name1); // 输出: undefined,无法访问另一个父类的属性
    console.log(child.name2); // 输出: Parent2